# Copyright (c) 2022 Robert Bosch GmbH and Microsoft Corporation
#
# This program and the accompanying materials are made available under the
# terms of the Apache License, Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# SPDX-License-Identifier: Apache-2.0

"""A sample skeleton vehicle app."""

import asyncio
import json
import logging
import signal


from sdv.util.log import (  # type: ignore
    get_opentelemetry_log_factory,
    get_opentelemetry_log_format,
)
from sdv.vdb.subscriptions import DataPointReply
from sdv.vehicle_app import VehicleApp, subscribe_topic
from sdv_model import Vehicle, vehicle  # type: ignore

import serial

# Configure the VehicleApp logger with the necessary log config and level.
logging.setLogRecordFactory(get_opentelemetry_log_factory())
logging.basicConfig(format=get_opentelemetry_log_format())
logging.getLogger().setLevel("DEBUG")
logger = logging.getLogger(__name__)

GET_SPEED_REQUEST_TOPIC = "sampleapp/getSpeed"
GET_SPEED_RESPONSE_TOPIC = "sampleapp/getSpeed/response"
DATABROKER_SUBSCRIPTION_TOPIC = "sampleapp/currentSpeed"

gSer = None


class SampleApp(VehicleApp):
    """
    Sample skeleton vehicle app.

    The skeleton subscribes to a getSpeed MQTT topic
    to listen for incoming requests to get
    the current vehicle speed and publishes it to
    a response topic.

    It also subcribes to the VehicleDataBroker
    directly for updates of the
    Vehicle.Speed signal and publishes this
    information via another specific MQTT topic
    """

    def __init__(self, vehicle_client: Vehicle):
        # SampleApp inherits from VehicleApp.
        super().__init__()
        self.Vehicle = vehicle_client
        self._idx = 0

    async def on_start(self):
        """Run when the vehicle app starts"""
        # This method will be called by the SDK when the connection to the
        # Vehicle DataBroker is ready.
        # Here you can subscribe for the Vehicle Signals update (e.g. Vehicle Speed).
        await self.Vehicle.Speed.subscribe(self.on_speed_change)
        LOOP.create_task(self.on_timer())
        LOOP.create_task(self.bt_handler())

    async def on_timer(self):
        logging.debug("TASK LOOP ...")
        while 1:
            logging.debug("task loopping ...")
            await self.publish_mqtt_event("sampleapp/tasks", json.dumps({"period tasks": self._idx}),)
            self._idx = self._idx + 1
            await asyncio.sleep(2.0)
        pass

    async def bt_handler(self):
        global gSer
        #{\"userId\":\"user1\", \"seatPos\":0, \"mirror\":0, \"fan\":50, \"aircell\":0, \"mirror_folding\":false, \"mirror_heat\":false}
        logger.debug("BT handler, interact with Mobile APP")
        while 1:
            try:
                gSer = serial.Serial("/dev/ttyUSB0",9600, timeout=1)
                logging.debug("BT module connected:" + str(gSer.isOpen()))
                while 1:
                    r_line = gSer.readline()
                    logging.debug("BT RECV:" + str(r_line))
                    app_dit = json.loads(r_line)
                    #Handle case here.
                    fan_speed = app_dit["fan"]
                    logging.debug("HVAC Fan speed:" + str(fan_speed))

                    user_id = app_dit["userId"]
                    logging.debug("Current user:" + str(user_id))
                    ##################################################################################
                    #User case
                    #if user_id == "user1":
                    #    logging.debug("============================= Current user's position = 120")
                    #    await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.set(120)
                    #if user_id == "user2":
                    #    logging.debug("============================= Current user's position = 150")
                    #    await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.set(150)
                    ##################################################################################
                    #User case, app slider control position.
                    seat_pos = app_dit["seatPos"]
                    logging.debug("seat position:" + str(seat_pos))
                    await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.set(int(seat_pos))

                    await asyncio.sleep(0.01)
                    await self.publish_mqtt_event("sampleapp/bt_cmd/reponse", json.dumps({"bt_cmd": str(app_dit)}),)
            except Exception as e:
                logger.error(f'BT module {type(e)}: e')
                await asyncio.sleep(0.1)
                pass
        
    async def on_speed_change(self, data: DataPointReply):
        """The on_speed_change callback, this will be executed when receiving a new
        vehicle signal updates."""
        # Get the current vehicle speed value from the received DatapointReply.
        # The DatapointReply containes the values of all subscribed DataPoints of
        # the same callback.
        vehicle_speed = data.get(self.Vehicle.Speed).value
        
        #Test cases: US1
        if vehicle_speed >30:
            await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.set(90)
        elif vehicle_speed <= 30:
            await self.Vehicle.Cabin.Seat.Row1.Pos1.Position.set(60)
        else:
            pass
        # Do anything with the received value.
        # Example:
        # - Publishes current speed to MQTT Topic (i.e. DATABROKER_SUBSCRIPTION_TOPIC).
        await self.publish_mqtt_event(
            DATABROKER_SUBSCRIPTION_TOPIC,
            json.dumps({"speed": vehicle_speed}),
        )

    @subscribe_topic("sampleapp/demo")
    async def on_demo(self, data: str) -> None:
        logger.debug("MQTT sub: trigged!")
        await self.publish_mqtt_event("sampleapp/demo/response", json.dumps({"period tasks": self._idx}),)
        pass

    @subscribe_topic(GET_SPEED_REQUEST_TOPIC)
    async def on_get_speed_request_received(self, data: str) -> None:
        """The subscribe_topic annotation is used to subscribe for incoming
        PubSub events, e.g. MQTT event for GET_SPEED_REQUEST_TOPIC.
        """

        # Use the logger with the preferred log level (e.g. debug, info, error, etc)
        logger.debug(
            "PubSub event for the Topic: %s -> is received with the data: %s",
            GET_SPEED_REQUEST_TOPIC,
            data,
        )

        # Getting current speed from VehicleDataBroker using the DataPoint getter.
        vehicle_speed = (await self.Vehicle.Speed.get()).value

        # Do anything with the speed value.
        # Example:
        # - Publishe the vehicle speed to MQTT topic (i.e. GET_SPEED_RESPONSE_TOPIC).
        await self.publish_mqtt_event(
            GET_SPEED_RESPONSE_TOPIC,
            json.dumps(
                {
                    "result": {
                        "status": 0,
                        "message": f"""Current Speed = {vehicle_speed}""",
                    },
                }
            ),
        )


async def main():
    """Main function"""
    logger.info("Starting SampleApp...")
    # Constructing SampleApp and running it.
    vehicle_app = SampleApp(vehicle)
    await vehicle_app.run()


LOOP = asyncio.get_event_loop()
LOOP.add_signal_handler(signal.SIGTERM, LOOP.stop)
LOOP.run_until_complete(main())
LOOP.close()
